package rcs34.android.AndroidRC;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.zip.Adler32;

import android.net.Uri;
import android.util.Base64;
import android.webkit.MimeTypeMap;

public class HttpMessage {
  private final static String DEF_VERSION = "HTTP/1.1";
  public final static String MIME_ICON = "image/x-icon";
  public final static String MIME_HTML = "text/html";
  public final static String MIME_TXT = "text/plain";
  public final static String MIME_JS = "application/javascript";
  public final static String MIME_CSS = "text/css";
  public final static String MIME_JSON = "application/json";
  public final static String MIME_PNG = "image/png";
  public final static String MIME_GIF = "image/gif";
  public final static String MIME_B64 = "application/base64";
  private final int BUFF_SIZE = 10240;
  public String method;
  public Uri uri;
  public String sUri;
  public String path;
  public String version = DEF_VERSION;
  public boolean keepAlive;
  public int contentLength;
  public HashMap<String, String> params = new HashMap<String, String>();
  private BufferedInputStream bis;
  private final byte[] headChars = new byte[1024];
  private final byte[] linePair = new byte[2];
  
  public HttpMessage(Socket _socket) {
    try {
      bis = new BufferedInputStream(_socket.getInputStream(), BUFF_SIZE);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  
  public void readMessage() throws SocketTimeoutException, IOException {
    params.clear();
    keepAlive = false;
    contentLength = 0;
    method = null;
    path = null;
    version = null;
    while (readHeaderLine() > 0)
      ;
    if (method == null || sUri == null || version == null) {
      System.out.println(method + "," + sUri + "," + version);
      throw (new IOException());
    }
    uri = Uri.parse(sUri);
    path = uri.getPath();
    if (path.equals("/")) {
      path = "/index.html";
    }
    GApp.inactivity.reset();
    String connection = params.get("connection");
    if (connection != null && connection.equals("keep-alive")) {
      keepAlive = true;
    }
    String sLength = params.get("content-length");
    if (sLength != null) {
      contentLength = Integer.parseInt(sLength);
    }
  }
  
  private int readHeaderLine() throws IOException {
    int lineLength = 0;
    int length = 0;
    int field = -1;
    boolean inField = false;
    String param = null;
    boolean drop = false;
    boolean expectField = true;
    // System.out.println("------------");
    while (bis.read(linePair, 1, 1) == 1) {
      // System.out.print(linePair[1]+"|");
      lineLength++;
      boolean eol = ((linePair[0] == '\r' && linePair[1] == '\n'));
      linePair[0] = linePair[1];
      if (!drop) {
        // Spaces and double dots - fields separators
        if (linePair[0] == ' ' || linePair[0] == ':' || linePair[0] == '\r'
            || linePair[0] == '\n') {
          // End of field
          if (inField) {
            inField = false;
            String sField = new String(headChars, 0, length);
            // System.out.println(headChars);
            length = 0;
            switch (field) {
            case 0:
              if (sField.equals("GET") || sField.equals("POST")) {
                method = sField;
              } else {
                sField = sField.toLowerCase();
                if (sField.equals("content-length")
                    || sField.equals("connection")) {
                  param = sField;
                } else { // Not interesting stuff
                  drop = true;
                }
              }
              break;
            case 1:
              if (param != null) {
                params.put(param, sField);
                expectField = false;
              } else {
                sUri = sField;
              }
              break;
            case 2:
              version = sField;
              expectField = false;
              break;
            }
          }
        } else { // Some non separation character
          if (!inField) { // Begin of a new field
            if (expectField) {
              inField = true;
              field++;
            } else {
              drop = true;
            }
          }
          if (length < headChars.length) {
            headChars[length] = linePair[0];
            length++;
          } else {
            drop = true;
          }
        }
      }
      if (eol) {
        return lineLength - 2;
      }
    }
    return -1;
  }
  
  public byte[] getContent() {
    byte[] content = null;
    if (contentLength > 0) {
      content = new byte[contentLength];
      try {
        int total = 0;
        int read = 0;
        while (total < contentLength
            && (read = bis.read(content, total, contentLength - total)) >= 0) {
          total += read;
        }
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    return content;
  }
  
  public void dropContent() {
    byte[] content = null;
    if (contentLength > 0) {
      content = new byte[BUFF_SIZE];
      try {
        int total = 0;
        int read = 0;
        while (total < contentLength && (read = bis.read(content)) >= 0) {
          total += read;
        }
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    return;
  }
  
  public boolean writeContentToFile(File output, long _position, long _adler32) {
    Adler32 adler32 = new Adler32();
    //System.out.println("cLength: "+contentLength);
    if (contentLength < 1) {
      return false;
    }
    //System.out.println(output.getAbsolutePath());
    FileOutputStream os = null;
    FileChannel channel = null;
    try {
      os = new FileOutputStream(output, true);
      channel = os.getChannel();
      channel.truncate(_position);
      byte[] buffer = new byte[BUFF_SIZE];
      int total = 0;
      int read = 0;
      while (total < contentLength && (read = bis.read(buffer)) >= 0) {
        total += read;
        os.write(buffer, 0, read);
        adler32.update(buffer, 0, read);
      }
       //System.out.println("Mine/his: "+adler32.getValue()+"/"+_adler32);
      if (_adler32 >= 0 && adler32.getValue() != _adler32) {
        channel.truncate(_position);
        return false;
      }
      return true;
    } catch (IOException e) {
      if (channel != null) {
        try {
          channel.truncate(_position);
        } catch (IOException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
      }
      e.printStackTrace();
    } finally {
      Utils.closeQuietly(os);
    }
    return false;
  }
  
  public BufferedInputStream getContentIs() {
    return bis;
  }
  
  public int getContentLength() {
    return contentLength;
  }
  
  public byte[] composeResponse(byte[] _data, String _mime, String _cacheControl) {
    String response = version + " " + HttpProcessor.RESP_OK
        + HttpProcessor.RESP_NEWLINE + "Content-Type: " + _mime
        + HttpProcessor.RESP_NEWLINE + "Content-Length: " + _data.length
        + HttpProcessor.RESP_NEWLINE + _cacheControl
        + HttpProcessor.RESP_NEWLINE;
    return response.getBytes();
  }
  
  public byte[] composeResponse(File _file) {
    MimeTypeMap map = MimeTypeMap.getSingleton();
    String name = _file.getName();
    String mime = map.getMimeTypeFromExtension(name.substring(name
        .lastIndexOf(".") + 1));
    String response = version + " " + HttpProcessor.RESP_OK
        + HttpProcessor.RESP_NEWLINE + "Content-Type: " + mime
        + HttpProcessor.RESP_NEWLINE + "Content-Length: " + _file.length()
        + HttpProcessor.RESP_NEWLINE + HttpProcessor.RESP_NEWLINE;
    // System.out.println(response);
    return response.getBytes();
  }
}
